Ingress Configuration

Argo CD runs both a gRPC server (used by the CLI), as well as a HTTP/HTTPS server (used by the UI). Both protocols are exposed by the argocd-server service object on the following ports:

  • 443 - gRPC/HTTPS
  • 80 - HTTP (redirects to HTTPS)

There are several ways how Ingress can be configured.

Ambassador

The Ambassador Edge Stack can be used as a Kubernetes ingress controller with automatic TLS termination and routing capabilities for both the CLI and the UI.

The API server should be run with TLS disabled. Edit the argocd-server deployment to add the --insecure flag to the argocd-server command. Given the argocd CLI includes the port number in the request host header, 2 Mappings are required.

Option 1: Mapping CRD for Host-based Routing

  1. apiVersion: getambassador.io/v2
  2. kind: Mapping
  3. metadata:
  4. name: argocd-server-ui
  5. namespace: argocd
  6. spec:
  7. host: argocd.example.com
  8. prefix: /
  9. service: argocd-server:443
  10. ---
  11. apiVersion: getambassador.io/v2
  12. kind: Mapping
  13. metadata:
  14. name: argocd-server-cli
  15. namespace: argocd
  16. spec:
  17. host: argocd.example.com:443
  18. prefix: /
  19. service: argocd-server:443

Login with the argocd CLI using the extra --grpc-web-root-path flag for gRPC-web.

  1. argocd login <host>:<port> --grpc-web-root-path /

Option 2: Mapping CRD for Path-based Routing

The API server must be configured to be available under a non-root path (e.g. /argo-cd). Edit the argocd-server deployment to add the --rootpath=/argo-cd flag to the argocd-server command.

  1. apiVersion: getambassador.io/v2
  2. kind: Mapping
  3. metadata:
  4. name: argocd-server
  5. namespace: argocd
  6. spec:
  7. prefix: /argo-cd
  8. rewrite: /argo-cd
  9. service: argocd-server:443

Login with the argocd CLI using the extra --grpc-web-root-path flag for non-root paths.

  1. argocd login <host>:<port> --grpc-web-root-path /argo-cd

Contour

The Contour ingress controller can terminate TLS ingress traffic at the edge.

The Argo CD API server should be run with TLS disabled. Edit the argocd-server Deployment to add the --insecure flag to the argocd-server container command.

It is also possible to provide an internal-only ingress path and an external-only ingress path by deploying two instances of Contour: one behind a private-subnet LoadBalancer service and one behind a public-subnet LoadBalancer service. The private Contour deployment will pick up Ingresses annotated with kubernetes.io/ingress.class: contour-external and the public Contour deployment will pick up Ingresses annotated with kubernetes.io/ingress.class: contour-external.

This provides the opportunity to deploy the Argo CD UI privately but still allow for SSO callbacks to succeed.

Private Argo CD UI with Multiple Ingress Objects and BYO Certificate

Since Contour Ingress supports only a single protocol per Ingress object, define three Ingress objects. One for private HTTP/HTTPS, one for private gRPC, and one for public HTTPS SSO callbacks.

Internal HTTP/HTTPS Ingress:

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-http
  5. annotations:
  6. kubernetes.io/ingress.class: contour-internal
  7. ingress.kubernetes.io/force-ssl-redirect: "true"
  8. spec:
  9. rules:
  10. - host: internal.path.to.argocd.io
  11. http:
  12. paths:
  13. - backend:
  14. serviceName: argocd-server
  15. servicePort: http
  16. tls:
  17. - hosts:
  18. - internal.path.to.argocd.io
  19. secretName: your-certificate-name

Internal gRPC Ingress:

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-grpc
  5. annotations:
  6. kubernetes.io/ingress.class: contour-internal
  7. spec:
  8. rules:
  9. - host: grpc-internal.path.to.argocd.io
  10. http:
  11. paths:
  12. - backend:
  13. serviceName: argocd-server
  14. servicePort: https
  15. tls:
  16. - hosts:
  17. - grpc-internal.path.to.argocd.io
  18. secretName: your-certificate-name

External HTTPS SSO Callback Ingress:

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-external-callback-http
  5. annotations:
  6. kubernetes.io/ingress.class: contour-external
  7. ingress.kubernetes.io/force-ssl-redirect: "true"
  8. spec:
  9. rules:
  10. - host: external.path.to.argocd.io
  11. http:
  12. paths:
  13. - path: /api/dex/callback
  14. backend:
  15. serviceName: argocd-server
  16. servicePort: http
  17. tls:
  18. - hosts:
  19. - external.path.to.argocd.io
  20. secretName: your-certificate-name

The argocd-server Service needs to be annotated with projectcontour.io/upstream-protocol.h2c: "https,443" to wire up the gRPC protocol proxying.

The API server should then be run with TLS disabled. Edit the argocd-server deployment to add the --insecure flag to the argocd-server command:

  1. spec:
  2. template:
  3. spec:
  4. containers:
  5. - name: argocd-server
  6. command:
  7. - /argocd-server
  8. - --staticassets
  9. - /shared/app
  10. - --repo-server
  11. - argocd-repo-server:8081
  12. - --insecure

kubernetes/ingress-nginx

Option 1: SSL-Passthrough

Argo CD serves multiple protocols (gRPC/HTTPS) on the same port (443), this provides a challenge when attempting to define a single nginx ingress object and rule for the argocd-service, since the nginx.ingress.kubernetes.io/backend-protocol annotation accepts only a single value for the backend protocol (e.g. HTTP, HTTPS, GRPC, GRPCS).

In order to expose the Argo CD API server with a single ingress rule and hostname, the nginx.ingress.kubernetes.io/ssl-passthrough annotation must be used to passthrough TLS connections and terminate TLS at the Argo CD API server.

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-ingress
  5. namespace: argocd
  6. annotations:
  7. kubernetes.io/ingress.class: nginx
  8. nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
  9. nginx.ingress.kubernetes.io/ssl-passthrough: "true"
  10. spec:
  11. rules:
  12. - host: argocd.example.com
  13. http:
  14. paths:
  15. - backend:
  16. serviceName: argocd-server
  17. servicePort: https

The above rule terminates TLS at the Argo CD API server, which detects the protocol being used, and responds appropriately. Note that the nginx.ingress.kubernetes.io/ssl-passthrough annotation requires that the --enable-ssl-passthrough flag be added to the command line arguments to nginx-ingress-controller.

SSL-Passthrough with cert-manager and Let’s Encrypt

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-ingress
  5. namespace: argocd
  6. annotations:
  7. cert-manager.io/cluster-issuer: letsencrypt-prod
  8. kubernetes.io/ingress.class: nginx
  9. kubernetes.io/tls-acme: "true"
  10. nginx.ingress.kubernetes.io/ssl-passthrough: "true"
  11. # If you encounter a redirect loop or are getting a 307 response code
  12. # then you need to force the nginx ingress to connect to the backend using HTTPS.
  13. #
  14. # nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
  15. spec:
  16. rules:
  17. - host: argocd.example.com
  18. http:
  19. paths:
  20. - backend:
  21. serviceName: argocd-server
  22. servicePort: https
  23. path: /
  24. tls:
  25. - hosts:
  26. - argocd.example.com
  27. secretName: argocd-secret # do not change, this is provided by Argo CD

Option 2: Multiple Ingress Objects And Hosts

Since ingress-nginx Ingress supports only a single protocol per Ingress object, an alternative way would be to define two Ingress objects. One for HTTP/HTTPS, and the other for gRPC:

HTTP/HTTPS Ingress:

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-http-ingress
  5. namespace: argocd
  6. annotations:
  7. kubernetes.io/ingress.class: "nginx"
  8. nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
  9. nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
  10. spec:
  11. rules:
  12. - http:
  13. paths:
  14. - backend:
  15. serviceName: argocd-server
  16. servicePort: http
  17. host: argocd.example.com
  18. tls:
  19. - hosts:
  20. - argocd.example.com
  21. secretName: argocd-secret # do not change, this is provided by Argo CD

gRPC Ingress:

  1. apiVersion: extensions/v1beta1
  2. kind: Ingress
  3. metadata:
  4. name: argocd-server-grpc-ingress
  5. namespace: argocd
  6. annotations:
  7. kubernetes.io/ingress.class: "nginx"
  8. nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
  9. spec:
  10. rules:
  11. - http:
  12. paths:
  13. - backend:
  14. serviceName: argocd-server
  15. servicePort: https
  16. host: grpc.argocd.example.com
  17. tls:
  18. - hosts:
  19. - grpc.argocd.example.com
  20. secretName: argocd-secret # do not change, this is provided by Argo CD

The API server should then be run with TLS disabled. Edit the argocd-server deployment to add the --insecure flag to the argocd-server command:

  1. spec:
  2. template:
  3. spec:
  4. containers:
  5. - name: argocd-server
  6. command:
  7. - argocd-server
  8. - --staticassets
  9. - /shared/app
  10. - --repo-server
  11. - argocd-repo-server:8081
  12. - --insecure

The obvious disadvantage to this approach is that this technique requires two separate hostnames for the API server — one for gRPC and the other for HTTP/HTTPS. However it allows TLS termination to happen at the ingress controller.

Traefik (v2.2)

Traefik can be used as an edge router and provide TLS termination within the same deployment.

It currently has an advantage over NGINX in that it can terminate both TCP and HTTP connections on the same port meaning you do not require multiple hosts or paths.

The API server should be run with TLS disabled. Edit the argocd-server deployment to add the --insecure flag to the argocd-server command.

IngressRoute CRD

  1. apiVersion: traefik.containo.us/v1alpha1
  2. kind: IngressRoute
  3. metadata:
  4. name: argocd-server
  5. namespace: argocd
  6. spec:
  7. entryPoints:
  8. - websecure
  9. routes:
  10. - kind: Rule
  11. match: Host(`argocd.example.com`)
  12. priority: 10
  13. services:
  14. - name: argocd-server
  15. port: 80
  16. - kind: Rule
  17. match: Host(`argocd.example.com`) && Headers(`Content-Type`, `application/grpc`)
  18. priority: 11
  19. services:
  20. - name: argocd-server
  21. port: 80
  22. scheme: h2c
  23. tls:
  24. certResolver: default
  25. options: {}

AWS Application Load Balancers (ALBs) And Classic ELB (HTTP Mode)

AWS ALBs can be used as an L7 Load Balancer for both UI and gRPC traffic, whereas Classic ELBs and NLBs can be used as L4 Load Balancers for both.

When using an ALB, you’ll want to create a second service for argocd-server. This is necessary because we need to tell the ALB to send the GRPC traffic to a different target group then the UI traffic, since the backend protocol is HTTP2 instead of HTTP1.

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. annotations:
  5. alb.ingress.kubernetes.io/backend-protocol-version: HTTP2 #This tells AWS to send traffic from the ALB using HTTP2. Can use GRPC as well if you want to leverage GRPC specific features
  6. labels:
  7. app: argogrpc
  8. name: argogrpc
  9. namespace: argocd
  10. spec:
  11. ports:
  12. - name: "443"
  13. port: 443
  14. protocol: TCP
  15. targetPort: 8080
  16. selector:
  17. app.kubernetes.io/name: argocd-server
  18. sessionAffinity: None
  19. type: ClusterIP

Once we create this service, we can configure the Ingress to conditionally route all application/grpc traffic to the new HTTP2 backend, using the alb.ingress.kubernetes.io/conditions annotation, as seen below. Note: The value after the . in the condition annotation must be the same name as the service that you want traffic to route to - and will be applied on any path with a matching serviceName.

  1. apiVersion: networking.k8s.io/v1 # Use extensions/v1beta1 for Kubernetes 1.14 and older
  2. kind: Ingress
  3. metadata:
  4. annotations:
  5. alb.ingress.kubernetes.io/backend-protocol: HTTPS
  6. # Use this annotation (which must match a service name) to route traffic to HTTP2 backends.
  7. alb.ingress.kubernetes.io/conditions.argogrpc: |
  8. [{"field":"http-header","httpHeaderConfig":{"httpHeaderName": "Content-Type", "values":["application/grpc"]}}]
  9. alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
  10. name: argocd
  11. namespace: argocd
  12. spec:
  13. rules:
  14. - host: argocd.argoproj.io
  15. http:
  16. paths:
  17. - backend:
  18. serviceName: argogrpc
  19. servicePort: 443
  20. pathType: ImplementationSpecific
  21. - backend:
  22. serviceName: argocd-server
  23. servicePort: 443
  24. pathType: ImplementationSpecific
  25. tls:
  26. - hosts:
  27. - argocd.argoproj.io

Authenticating through multiple layers of authenticating reverse proxies

ArgoCD endpoints may be protected by one or more reverse proxies layers, in that case, you can provide additional headers through the argocd CLI --header parameter to authenticate through those layers.

  1. $ argocd login <host>:<port> --header 'x-token1:foo' --header 'x-token2:bar' # can be repeated multiple times
  2. $ argocd login <host>:<port> --header 'x-token1:foo,x-token2:bar' # headers can also be comma separated

ArgoCD Server and UI Root Path (v1.5.3)

ArgoCD server and UI can be configured to be available under a non-root path (e.g. /argo-cd). To do this, add the --rootpath flag into the argocd-server deployment command:

  1. spec:
  2. template:
  3. spec:
  4. name: argocd-server
  5. containers:
  6. - command:
  7. - /argocd-server
  8. - --staticassets
  9. - /shared/app
  10. - --repo-server
  11. - argocd-repo-server:8081
  12. - --rootpath
  13. - /argo-cd

NOTE: The flag --rootpath changes both API Server and UI base URL. Example nginx.conf:

  1. worker_processes 1;
  2. events { worker_connections 1024; }
  3. http {
  4. sendfile on;
  5. server {
  6. listen 443;
  7. location /argo-cd/ {
  8. proxy_pass https://localhost:8080/argo-cd/;
  9. proxy_redirect off;
  10. proxy_set_header Host $host;
  11. proxy_set_header X-Real-IP $remote_addr;
  12. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  13. proxy_set_header X-Forwarded-Host $server_name;
  14. # buffering should be disabled for api/v1/stream/applications to support chunked response
  15. proxy_buffering off;
  16. }
  17. }
  18. }

Flag --grpc-web-root-path is used to provide a non-root path (e.g. /argo-cd)

  1. $ argocd login <host>:<port> --grpc-web-root-path /argo-cd

UI Base Path

If the Argo CD UI is available under a non-root path (e.g. /argo-cd instead of /) then the UI path should be configured in the API server. To configure the UI path add the --basehref flag into the argocd-server deployment command:

  1. spec:
  2. template:
  3. spec:
  4. name: argocd-server
  5. containers:
  6. - command:
  7. - /argocd-server
  8. - --staticassets
  9. - /shared/app
  10. - --repo-server
  11. - argocd-repo-server:8081
  12. - --basehref
  13. - /argo-cd

NOTE: The flag --basehref only changes the UI base URL. The API server will keep using the / path so you need to add a URL rewrite rule to the proxy config. Example nginx.conf with URL rewrite:

  1. worker_processes 1;
  2. events { worker_connections 1024; }
  3. http {
  4. sendfile on;
  5. server {
  6. listen 443;
  7. location /argo-cd {
  8. rewrite /argo-cd/(.*) /$1 break;
  9. proxy_pass https://localhost:8080;
  10. proxy_redirect off;
  11. proxy_set_header Host $host;
  12. proxy_set_header X-Real-IP $remote_addr;
  13. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  14. proxy_set_header X-Forwarded-Host $server_name;
  15. # buffering should be disabled for api/v1/stream/applications to support chunked response
  16. proxy_buffering off;
  17. }
  18. }
  19. }